package jch.learn.java.advanced.chapter1_stream.collecting;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

// 下游收集器 | 群组和分区
public class DownstreamCollectors {

    public static class City {
        private String name;
        private String state;
        private int population;

        public City(String name, String state, int population) {
            this.name = name;
            this.state = state;
            this.population = population;
        }

        public String getName() {
            return name;
        }

        public String getState() {
            return state;
        }

        public int getPopulation() {
            return population;
        }
    }

    public static Stream<City> readCities(String filename) throws IOException, URISyntaxException {
        return Files.lines(Paths.get(DownstreamCollectors.class.getResource("/"+filename).toURI())).map(l -> l.split(",")).map(a -> new City(a[0], a[1], Integer.parseInt(a[2].trim())));
    }

    public static void main(String[] args) throws IOException, URISyntaxException {
        // java.util.stream.Collector
        // static <T,K> Collector<T,?,Map<K,List<T>>> groupingBy(Function<? super T,? extends K> classifier)
        // static <T,K> Collector<T,?,ConcurrentMap<K,List<T>>> groupingByConcurrent(Function<? super T,? extends K> classifier)
        // 产生一个映射表或并发映射表，其键是将classifier应用于所有收集到的元素上所产生的结果，而值是由具有相同键的元素构成的一个个列表。
        // static <T> Collector<T,?,Map<Boolean,List<T>>> partitioningBy(Predicate<? super T> predicate)
        // 产生一个映射表，其键是true/false,而值是由满足/不满足断言的元素构成的列表

        // static <T> Collector<T,?,Long> counting()
        // 产生一个可以收集到的元素进行计数的收集器
        // static <T> Collector<T,?,Integer> summingInt(ToIntFunction<? super T> mapper)
        // static <T> Collector<T,?,Long> summingLong(ToLongFunction<? super T> mapper)
        // static <T> Collector<T,?,Double> summingDouble(ToDoubleFunction<? super T> mapper)
        // 将mapper应用到收集到的元素上之后产生的值计算总和
        // static <T> Collector<T,?,Optional<T>> maxBy(Comparator<? super T> comparator)
        // static <T> Collector<T,?,Optional<T>> minBy(Comparator<? super T> comparator)
        // 使用comparator指定的排序方法，计算收集到的元素的最大值和最小值
        // static <T,U,A,R> Collector<T,?,R> mapping(Function<? super T,? extends U> mapper,Collector<? super U,A,R> downstream)
        // 产生一个映射表，其键是将mapper应用到收集到的数据上而产生的，其值是使用downstream收集器收集到的具有相同键的元素

        Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
        Map<String, Set<Locale>> countryToLocaleSet = locales.collect(groupingBy(Locale::getCountry, toSet()));
        System.out.println("countryToLocaleSet: " + countryToLocaleSet);

        locales = Stream.of(Locale.getAvailableLocales());
        Map<String, Long> countryToLocaleCounts = locales.collect(groupingBy(Locale::getCountry, counting()));
        System.out.println("countryToLocaleCounts: " + countryToLocaleCounts);

        Stream<City> cities = readCities("cities.txt");
        Map<String, Integer> stateToCityPopulation = cities.collect(groupingBy(City::getState, summingInt(City::getPopulation)));
        System.out.println("stateToCityPopulation: " + stateToCityPopulation);

        cities = readCities("cities.txt");
        Map<String, Optional<String>> stateToLongestCityName = cities.collect(groupingBy(City::getState, mapping(City::getName, maxBy(Comparator.comparing(String::length)))));
        System.out.println("stateToLongestCityName: " + stateToLongestCityName);

        locales = Stream.of(Locale.getAvailableLocales());;
        Map<String, Set<String>> countryToLanguages = locales.collect(groupingBy(Locale::getDisplayCountry, mapping(Locale::getDisplayLanguage, toSet())));
        System.out.println("countryToLanguages: " + countryToLanguages);

        cities = readCities("cities.txt");
        Map<String, IntSummaryStatistics> stateToCityPopulationSummary = cities.collect(groupingBy(City::getState, summarizingInt(City::getPopulation)));
        System.out.println(stateToCityPopulationSummary.get("NY"));

        cities = readCities("cities.txt");
        Map<String, String> stateToCityNames = cities.collect(groupingBy(City::getState, reducing("", City::getName, (s, t) -> s.length() == 0 ? t : s + "," + t)));

        cities = readCities("cities.txt");
        stateToCityNames = cities.collect(groupingBy(City::getState, mapping(City::getName, joining(","))));
        System.out.println("stateToCityNames: " + stateToCityNames);
    }
}
